package com.yixing.tech.common.base;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.yixing.tech.common.config.IgnoreRequestLogMap;
import com.yixing.tech.common.enums.ErrCode;
import com.yixing.tech.common.utils.CommonConstant;
import com.yixing.tech.common.utils.CommonUtil;
import com.yixing.tech.common.vo.s3.DepartmentVO;
import com.yixing.tech.common.vo.s3.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author yixing tech
 */
@Deprecated
@Component
@Slf4j
public class BaseControlBak {

    public final static String CHARSET_UTF8 = "UTF-8";
    private final static long logLength = 2000;

    @Value("${official-tenant-id}")
    public String officialTenantId;
    private IgnoreRequestLogMap ignoreRequestLogMap = new IgnoreRequestLogMap();
    @Resource
    private MongoTemplate mongoTemplate;
    @Value("${log-obj-id}")
    private String logObjId;
    @Value("${tenant-id: 6204cb763da841718ddd1cf8}")
    private String tenantId;

    @Resource
    public StringRedisTemplate stringRedisTemplate;

    @Resource
    private MessageSource messageSource;

    public void sendResponse() {
        sendResponse(new BaseResponse(), CHARSET_UTF8,true,false);
    }

    public void sendResponse(boolean isLog) {
        sendResponse(new BaseResponse(), CHARSET_UTF8, isLog,false);
    }

    public void sendResponse(BaseResponse baseResponse) {
        sendResponse(baseResponse, CHARSET_UTF8,true,false);

    }

    public void sendResponse(Object obj) {
        sendResponse(new BaseResponse(obj),CHARSET_UTF8,true,false);
    }

    public void sendResponse(BaseResponse baseResponse, boolean isLog) {
        sendResponse(baseResponse, CHARSET_UTF8, isLog,false);
    }

    public void sendResponse(Object obj, String charSet) {
        sendResponse(new BaseResponse(obj), charSet, true,false);
    }

    public void sendResponse(Object obj, boolean isLog,boolean containNull) {
        sendResponse(new BaseResponse(obj), CHARSET_UTF8, isLog, containNull);
    }
    public void sendResponse(Object obj, String charSet, boolean isLog) {
        sendResponse(new BaseResponse(obj), charSet, isLog, false);
    }

    public void sendResponse(Object obj, String charSet, boolean isLog, boolean containNull) {
        if (obj == null) {
            return;
        }
        PrintWriter writer = null;
        HttpServletRequest request = getRequest();
        HttpServletResponse response = getResponse();
        response.addHeader("Content-Type", "application/json;charset="
                + charSet);
        response.setHeader("Connection", "keep-alive");
        try {
            writer = response.getWriter();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String result = "";
        if (obj instanceof String) {
            result = String.valueOf(obj);
        } else if (obj instanceof UserVO) {
            result = obj.toString();
        } else {
            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(new JavaTimeModule());
            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            if (!containNull)
                mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            try {
                result = mapper.writeValueAsString(obj);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        if (isLog) {
            String url = request.getRequestURI();
            String ip = getIp();
            StringBuilder builder = new StringBuilder();
            builder.append("response|||");

            builder.append("[clientip]:" + ip);
            builder.append("[url]:" + url);
            if (result.length() < logLength) {
                builder.append("[body]:" + result);
            } else {
                builder.append("[body]: data length too large");
            }
            log.info(builder.toString());
            recordLog(obj, request);
        }
        writer.write(result);
    }

    /**
     * 日志存储
     */
    @Async
    void recordLog(Object responseBody, HttpServletRequest request) {
        if (!(responseBody instanceof BaseResponse)) return;
        Map<String, Object> logMap = new HashMap<>();
        BaseResponse response = (BaseResponse) responseBody;
        String path = request.getRequestURI();
        String method = request.getMethod();
        int resultCode = response.getCode();
        logMap.put("结果", resultCode);
        logMap.put("路径", path);
        logMap.put("IP地址",getIp());
        logMap.put("类型", path.contains("obj/entity") ? "三方" : "平台");
        logMap.put("请求类型", method);
        logMap.put("createTime", new Date());
        logMap.put("tenantId", tenantId);
        try {
            UserVO userVO = getApiUser();
            if (userVO != null) {
                logMap.put("createName", userVO.getName());
                logMap.put("createId", userVO.getId());
                logMap.put("tenantId", userVO.getTenantId());
            }
        } catch (Exception e) {
        }

        boolean logable = true;
        for (Map.Entry<String, String> entry : ignoreRequestLogMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (!path.contains(key)) continue;
            if (null == value || method.equals(value)) {
                logable = false;
                break;
            }
        }
        if (!logable) return;
        mongoTemplate.insert(logMap, "entity_" + logObjId);
    }


    @ExceptionHandler({Exception.class})
    public void exception(HttpServletRequest request, HttpServletResponse response, Exception e) {
        BaseResponse baseResponse = new BaseResponse();
        if (e instanceof BusinessException) {
            BusinessException be = (BusinessException) e;
            int code = be.getErrorCode();
            String message = e.getMessage();
            String defaultMsg = !CommonUtil.stringIsBlank(be.getMsg()) ? be.getMsg().toString() : message;
            String msg = messageSource.getMessage(String.valueOf(code), null, defaultMsg, Locale.getDefault());
            baseResponse.setDesc(msg);
            baseResponse.setCode(be.getErrorCode());
            sendResponse(baseResponse);
        } else if (e instanceof MethodArgumentNotValidException) {
            String message = ((MethodArgumentNotValidException)e).getBindingResult().getFieldError().getDefaultMessage();
            log.error(message, e);
            baseResponse.setDesc(message);
            baseResponse.setCode(ErrCode.PARAMETER_ERROR.getCode());
            sendResponse(baseResponse);
        } else if (e instanceof MissingServletRequestParameterException) {
            String message = e.getMessage();
            log.error(message, e);
            baseResponse.setDesc(message);
            baseResponse.setCode(ErrCode.PARAMETER_ERROR.getCode());
            sendResponse(baseResponse);
        } else if(e instanceof UncategorizedMongoDbException){
            String message = e.getMessage();
            log.error(message, e);
            baseResponse.setDesc("查询超时");
            baseResponse.setCode(ErrCode.INTERNAL_SERVER_ERROR.getCode());
            sendResponse(baseResponse);
        }else {
            log.error("error", e);
            baseResponse.setDesc("后台异常");
            baseResponse.setCode(ErrCode.INTERNAL_SERVER_ERROR.getCode());
            sendResponse(baseResponse);
        }

    }


    public String getIp() {
        HttpServletRequest request = getRequest();
        String ip = request.getHeader(CommonConstant.HEADERS_IP);
        if (CommonUtil.stringIsBlank(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


    public UserVO getApiUser(String apiToken) throws BusinessException {
        String json = stringRedisTemplate.opsForValue().get(CommonConstant.USERTOKEN+apiToken);
        if (ObjectUtils.isEmpty(json)) {
            throw new BusinessException(ErrCode.TOKEN_INVALID);
        }
        UserVO user = JSONObject.parseObject(json, UserVO.class);
        stringRedisTemplate.expire(CommonConstant.USERTOKEN+apiToken,
                CommonConstant.REDIS_APIUSER_EXPIRE, TimeUnit.SECONDS);
        return user;
    }

    public UserVO getApiUser() throws BusinessException {
        String apiToken = getApiToken();
        return getApiUser(apiToken);
    }

    public void reflashApiUser(UserVO obj, String apiToken,String client) throws BusinessException {
        //踢出
        String oldToken = stringRedisTemplate.opsForValue().get(CommonConstant.REDIS_USERID_PREFIX+client+":" + obj.getId());
        if(!ObjectUtils.isEmpty(oldToken)){
            stringRedisTemplate.delete(CommonConstant.USERTOKEN+oldToken);
        }
        String json = JSONObject.toJSONString(obj);
        stringRedisTemplate.opsForValue().set(CommonConstant.USERTOKEN+apiToken,
                json, CommonConstant.REDIS_APIUSER_EXPIRE, TimeUnit.SECONDS);
        stringRedisTemplate.opsForValue().set(CommonConstant.REDIS_USERID_PREFIX+client+":" + obj.getId(),
                apiToken, CommonConstant.REDIS_USERID_EXPIRE, TimeUnit.SECONDS);

    }

    public String createToken() {
        String token = CommonUtil.createUuid();
        return token;
    }

    public String getApiToken() throws BusinessException {
        HttpServletRequest request = getRequest();
        String value = request.getHeader(CommonConstant.HEADERS_X_USER);
        if (CommonUtil.stringIsBlank(value)) {
            throw new BusinessException(ErrCode.TOKEN_INVALID);
        }
        return value;
    }


    public HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        return request;
    }

    public HttpServletResponse getResponse() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        return response;
    }

    /**
     * 创建人
     *
     * @param vo
     * @throws BusinessException
     */
    public void  fillCreateUser(BaseVO vo) throws BusinessException {
        UserVO userVO = getApiUser();
        if (userVO != null) {
            vo.setCreateId(userVO.getId());
            vo.setCreateName(userVO.getName());
            vo.setTenantId(userVO.getTenantId());
            DepartmentVO departmentVO = userVO.getDepartment();
            if (departmentVO != null)
                vo.setDepartmentId(departmentVO.getId());
        }
    }

    /**
     * 创建人
     *
     * @param vo
     * @throws BusinessException
     */
    public void  fillCreateUser(Map vo) throws BusinessException {
        UserVO userVO = getApiUser();
        if (userVO != null) {
            vo.put("createId", userVO.getId());
            vo.put("createName", userVO.getName());
            vo.put("tenantId", userVO.getTenantId());
            DepartmentVO departmentVO = userVO.getDepartment();
            if (departmentVO != null)
                vo.put("departmentId", departmentVO.getId());
            Date date = new Date();
            vo.put("createTime", date);
            vo.put("updateTime", date);

        }
    }

    /**
     * 更新人
     *
     * @param vo
     * @throws BusinessException
     */
    public void fillUpdateUser(BaseVO vo) throws BusinessException {
        UserVO userVO = getApiUser();
        if (userVO != null) {
            vo.setUpdateId(userVO.getId());
            vo.setUpdateName(userVO.getName());
            Date date = new Date();
            vo.setUpdateTime(date);
        }
    }

    /**
     * 更新人
     *
     * @param vo
     * @throws BusinessException
     */
    public void fillUpdateUser(Map vo) throws BusinessException {
        UserVO userVO = getApiUser();
        if (userVO != null) {
            vo.put("updateId", userVO.getId());
            vo.put("updateName", userVO.getName());
            Date date = new Date();
            vo.put("updateTime", date);
        }
    }

    /**
     * @throws BusinessException
     */
    public String getTenantId() throws BusinessException {
        UserVO userVO = getApiUser();
        return userVO.getTenantId();
    }

}
